package client;

import interfaces.ICallableMethod;
import interfaces.ICommunication;
import interfaces.XMLRMISerializable;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;

import objet.IHorloge;
import objet.IObjet1;
import objet.IPoint;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import tools.Communication;
import tools.Constantes;
import tools.XMLTool;

/**
 *  
 * Cette classe contient les appels de méthodes du client vers le serveur.
 * Il y a une méthode pour chaque méthode disponible coté serveur. Cela permet 
 * de discriminer les méthodes et de créer un methodCall simplement, étant donné
 * que l'on connaît les types d'arguments à passés.
 * 
 * @author Kevin Pozderec, Faycal Abka
 */
public class MethodCallImpl implements ICallableMethod{
	
	/**la représentation objet du serveur**/
	private InetAddress serveur ;
	
	/**le port utilisé pour la communication**/
	private int port ;	
	
	/**chaine de caractère correspondant au methodCall*/
	private StringBuffer xmlMethodCall ;
	
	/**chaine de caractère correspondant au methodResponse*/
	private String methodResponse;		
	
	/**Classe servant d'outil de communication par socket entre le client et le serveur*/
	private ICommunication communicationTool;
	
	/**Constructeur qui créer l'objet serveur et initie le port de communication à utiliser*/
	public MethodCallImpl (){
		try {
			
			serveur = InetAddress.getByName(Communication.adresseIPServeur);
			port = Communication.portCommunication;
			
		} catch (UnknownHostException e) {
			System.err.println("Erreur lors de la création de l'instance de MethodCallImpl");
			e.printStackTrace();
		}
	}	
	
	/**
	 * 
	 * Envoi un methodCall correspond à la méthode serveur pointMove qui repositionne un point aux coordonnées x et y.
	 * Cette méthode créer le XML methodCall, envoi au serveur la chaîne, récupère un methodResponse de la part du serveur
	 * et met à jour l'objet point si il n'y a pas eu d'erreur.
	 * 
	 * @param point : Objet de type IPoint
	 * @param newX : nouvelle coordonnée suivant X pour le point
	 * @param newY : nouvelle coordonnée suivant Y pour le point
	 */
	@Override
	public void pointMoveCall(IPoint point, int newX, int newY) {				
		
		/**Test si l'objet point est bien défini en tant que XMLRMISerializable, sinon stop le programme et affiche un message d'erreur*/
		if ( !testXMLRMISerializable(point.getClass())){
			System.out.println("Erreur : paramètre point n'est pas sérialisable, oubli?");
			return ;
		}
						
		/*Création du methodCall correspondant à un appel à pointMove*/		
		xmlMethodCall = new StringBuffer();
		xmlMethodCall.append("<methodCall><methodName>pointMove</methodName>" );
		xmlMethodCall.append("<params>" );
		xmlMethodCall.append("<param><value>"+((XMLRMISerializable)point).toXML());
		xmlMethodCall.append("</value></param>");
		xmlMethodCall.append("<param><value><int>"+newX+"</int></value></param>");
		xmlMethodCall.append("<param><value><int>"+newY+"</int></value></param>");
		xmlMethodCall.append("</params></methodCall>");		
		
		/*Envoi du methodCall au serveur et attente d'un methodResponse*/
		communicationTool = new Communication();
		methodResponse = communicationTool.communication(serveur, port, xmlMethodCall.toString(), true);
		
		if ( methodResponse == null ){
			System.err.println("Erreur : Pas de methodResponse. Fin du programme");
			return;
		}
		
		/*Vérification de la validé du xml reçu*/
		if ( !ValidationRNG(methodResponse) )
			return;		
		
		/*Transformation de la chaîne xml methodResponse en DOM pour la manipuler plus facilement*/
		Document d = XMLTool.XMLtoDOM(methodResponse);
		
		/*Identifie si il s'agît ou non d'un methodResponde d'erreur*/
		Element racine = d.getDocumentElement();
		if(racine.getFirstChild().getNodeName().equals("fault")){
			System.out.println("Il y a eu une erreur coté serveur. Fin de l'execution");
			return;
		}
			
		/*Mise à jour de l'objet point. Seul les objets que l'on a passés en paramètre peuvent être mis à jour*/
		((XMLRMISerializable)point).updateFromXML(d.getDocumentElement());		
		
	}

	/**
	 * 
	 * Envoi un methodCall correspondant à la méthode serveur useObject1 qui affiche plusieurs types d'arguments.
	 * Cette méthode créer le XML methodCall, envoie au serveur la chaîne, récupère un methodResponse de la part du serveur
	 * et met à jour l'objet point si il n'y a pas eu d'erreur.
	 * 
	 * @param o : Objet de type IObjet1
	 * @param a : entier quelconque
	 * @param b : booléen quelconque
	 * @param c : chaîne de caractère quelconque
	 * @param d : double quelconque
	 */
	@Override
	public void useObject1Call(IObjet1 o, int a, boolean b, String c, double d) {	
		
		/**Test si l'objet point est bien défini en tant que XMLRMISerializable, sinon stop le programme et affiche un message d'erreur*/
		if ( !testXMLRMISerializable(o.getClass())){
			System.out.println("Erreur : paramètre o n'est pas sérialisable");
			return ;
		}
		
		/*Création du methodCall correspondant à un appel à useObject1*/

		xmlMethodCall = new StringBuffer();	
		xmlMethodCall.append("<methodCall><methodName>useObject1</methodName>" );
		xmlMethodCall.append("<params>" );
		xmlMethodCall.append("<param><value>"+((XMLRMISerializable)o).toXML());
		xmlMethodCall.append("</value></param>");
		xmlMethodCall.append("<param><value><int>"+a+"</int></value></param>");
		
		/*Transformation du booléen vers le xml correspondant (true = 1 , false = 0)*/
		xmlMethodCall.append("<param><value><boolean>");
		if ( b )
			xmlMethodCall.append(1);
		else
			xmlMethodCall.append(0);
		xmlMethodCall.append("</boolean></value></param>");	
		
		xmlMethodCall.append("<param><value><string>"+c+"</string></value></param>");
		xmlMethodCall.append("<param><value><double>"+d+"</double></value></param>");
		xmlMethodCall.append("</params></methodCall>");	
		
		
		/*Envoi du methodCall au serveur et attente d'un methodResponse*/
		communicationTool = new Communication();
		methodResponse = communicationTool.communication(serveur, port, xmlMethodCall.toString(), true);
		
		if ( methodResponse == null ){
			System.err.println("Erreur : Pas de methodResponse reçu. Fin du programme");
			return;
		}
		
		/*Vérification de la validité du xml reçu*/
		if ( !ValidationRNG(methodResponse) )
			return;		
		
		/*Transformation de la chaîne xml methodResponse en DOM pour la manipuler plus facilement*/
		Document doc = XMLTool.XMLtoDOM(methodResponse);
		
		/*Identifie si il s'agît ou non d'un methodResponde d'erreur*/
		Element racine = doc.getDocumentElement();
		if(racine.getFirstChild().getNodeName().equals("fault")){
			System.out.println("Il y a eu une erreur coté serveur. Fin de l'execution");
			return;
		}
			
		/*Mise à jour de l'objet objet1*/
		((XMLRMISerializable)o).updateFromXML(doc.getDocumentElement());		
	}
	
	/**
	 * 
	 * Envoi un methodCall correspond à la méthode serveur pingDate qui affiche un type horloge qui manipule le type complexe Date.
	 * Cette méthode créer le XML methodCall, envoi au serveur la chaîne, récupère un methodResponse de la part du serveur
	 * et met à jour l'objet h si il n'y a pas eu d'erreur.
	 * 
	 * @param h : objet IHorloge à utiliser
	 */
	@Override
	public void pingDateCall(IHorloge h) {
		/**Test si l'objet horloge est bien défini en tant que XMLRMISerializable, sinon stop le programme et affiche un message d'erreur*/
		if ( !testXMLRMISerializable(h.getClass())){
			System.out.println("Erreur : paramètre h n'est pas sérialisable");
			return ;
		}
		
		/*Création du methodCall correspondant à un appel à useObject1*/

		xmlMethodCall = new StringBuffer();	
		xmlMethodCall.append("<methodCall><methodName>pingDate</methodName>" );
		xmlMethodCall.append("<params>" );
		xmlMethodCall.append("<param><value>"+((XMLRMISerializable)h).toXML());
		xmlMethodCall.append("</value></param>");
		xmlMethodCall.append("</params></methodCall>");	
		
		
		/*Envoi du methodCall au serveur et attente d'un methodResponse*/
		communicationTool = new Communication();
		methodResponse = communicationTool.communication(serveur, port, xmlMethodCall.toString(), true);
		
		if(methodResponse == null){
			System.err.println("Erreur : Pas de methodResponse reçue. Fin du programme");
			return;
		}
		
		/*Vérification de la validé du xml reçu*/
		if ( !ValidationRNG(methodResponse) )
			return;		
		
		/*Transformation de la chaîne xml methodResponse en DOM pour la manipuler plus facilement*/
		Document doc = XMLTool.XMLtoDOM(methodResponse);
		
		/*Identifie si il s'agît ou non d'un methodResponde d'erreur*/
		Element racine = doc.getDocumentElement();
		if(racine.getFirstChild().getNodeName().equals("fault")){
			System.out.println("Il y a eu une erreur coté serveur. Fin de l'execution");
			return;
		}
			
		/*Mise à jour de l'objet objet1*/
		((XMLRMISerializable)h).updateFromXML(doc.getDocumentElement());
	}
	
	
	/**
	 * Méthode qui vérifie qu'une classe est bien XMLRMISerializable
	 * 
	 * @param cl type classe générique
	 * @return vrai si c'est bien XMLRMISerializable, faux sinon
	 */
	public boolean testXMLRMISerializable ( Class<?> cl ){
		
		Class <?> [] tabInterface = cl.getInterfaces();
		if ( tabInterface.length == 0 )
			return false;
		for( int i=0;i<tabInterface.length;i++ ){
			if ( tabInterface[i].getName().equals(Constantes.chemin_XMLRMISerializable) ){
				return true;
			}
		}
		
		return false;
	}
	
	/**
	 * Méthode permettant de vérifier la conformité d'un xml par rapport au relaxNG
	 * @param xml : chaîne XML à vérifier
	 * @return true si valide false sinon
	 */
	public boolean ValidationRNG ( String xml ){
		
		try {
			/*Etape de validation de la chaîne réponse pour vérifier qu'elle correspond bien à une chaîne respectant le RelaxNG XMLRMI*/
			return XMLTool.validerRelaxNG(Constantes.chemin_RelaxNG_XMLRMI, xml);
			
		} catch (SAXException e) {
			System.err.println("Erreur : Le client a reçu une chaîne réponse ne correspondant pas à la syntaxe RelaxNG");
			System.err.println("Fin du programme");
			return false;
		} catch (IOException e) {
			System.err.println("Erreur : Problème pour la lecture du fichier relaxNG, impossible de vérifier celle-ci");
			System.err.println("Fin du programme");
			return false;
		}		
		
	}

	
	

}
